package com.tengju.support.infrastructure.persistence.redis;


import com.tengju.user.domain.shared.JsonUtil;
import com.tengju.support.infrastructure.persistence.redis.dataobject.RedisKeyObject;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component("supportRedisService")
public class RedisService {
    
    private static final Logger log = LoggerFactory.getLogger(RedisService.class);
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ValueOperations<String, Object> valueOperations;
    
    @Autowired
    private HashOperations<String, String, Object> hashOperations;
    
    @Autowired
    private ListOperations<String, Object> listOperations;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 获取锁
     */
    protected RLock getLock(RedisKeyObject key) {
        try {
            return redissonClient.getLock(key.getValue());
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }



    /**
     * 是否包含key
     *
     * @param key
     * @return
     */
    protected Boolean hasKey(RedisKeyObject key) {
        try {
            return redisTemplate.hasKey(key.getValue());
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 删除某个key
     *
     * @param key
     * @return
     */
    public Long delKey(RedisKeyObject key) {
        try {
            redisTemplate.delete(key.getValue());
            return 1L;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 删除多个key
     *
     * @param keys
     * @return
     */
    protected Long delKey(RedisKeyObject... keys) {
        try {
            Set<String> keySet = Stream.of(keys).map(RedisKeyObject::getValue).collect(Collectors.toSet());
            redisTemplate.delete(keySet);
            return (long)keySet.size();
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 删除key的集合
     *
     * @param keys
     * @return
     */
    protected Long delKey(Collection<RedisKeyObject> keys) {
        try {
            Set<String> keySet = keys.stream().map(RedisKeyObject::getValue).collect(Collectors.toSet());
            redisTemplate.delete(keySet);
            return (long)keySet.size();
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 设置key的生命周期TTL
     *
     * @param key
     * @param timeout
     * @param timeUnit
     * @return
     */
    protected Boolean expireKey(RedisKeyObject key, long timeout, TimeUnit timeUnit) {
        try {
            return redisTemplate.expire(key.getValue(), timeout, timeUnit);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 指定key的过期日期
     *
     * @param key
     * @param date
     * @return
     */
    protected Boolean expireKeyAt(RedisKeyObject key, Date date) {
        try {
            return redisTemplate.expireAt(key.getValue(), date);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 添加不带过期时间的缓存
     *
     * @param key
     * @param obj
     * @return
     */
    protected <T> Long setValue(RedisKeyObject key, T obj) {
        try {
            valueOperations.set(key.getValue(), obj);
            return 1L;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 添加带TTL的缓存
     *
     * @param key
     * @param obj
     * @param timeout
     * @param timeUnit
     * @return
     */
    public  <T> Long setValue(RedisKeyObject key, T obj, long timeout, TimeUnit timeUnit) {
        try {
            valueOperations.set(key.getValue(), obj, timeout, timeUnit);
            return 1L;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 设置hash中某个属性
     *
     * @param key
     * @param hashKey
     * @param hashValue
     * @return
     */
    protected <T> Long hashPut(RedisKeyObject key, String hashKey, T hashValue) {
        try {
            hashOperations.put(key.getValue(), hashKey, hashValue);
            return 1L;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 将map中的键值对全部添加到hash中
     *
     * @param key
     * @param map
     * @return
     */
    protected Long hashPutAll(RedisKeyObject key, Map<String, Object> map) {
        try {
            hashOperations.putAll(key.getValue(), map);
            return 1L;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * 判断hash中是否包含某个key
     *
     * @param key
     * @param hashKey
     * @return
     */
    protected Boolean hashHas(RedisKeyObject key, String hashKey) {
        try {
            return hashOperations.hasKey(key.getValue(), hashKey);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * hash中的某个field增加某个long数值
     *
     * @param key
     * @param hashKey
     * @param num
     * @return
     */
    protected Long hashIncrement(RedisKeyObject key, String hashKey, long num) {
        try {
            return hashOperations.increment(key.getValue(), hashKey, num);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }
    
    /**
     * hash中的某个field增加某个double数值
     *
     * @param key
     * @param hashKey
     * @param num
     * @return
     */
    protected Double hashIncrement(RedisKeyObject key, String hashKey, double num) {
        try {
            return hashOperations.increment(key.getValue(), hashKey, num);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> Long leftPush(RedisKeyObject key, T obj) {
        try {
            return listOperations.leftPush(key.getValue(), obj);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> Long leftPushAll(RedisKeyObject key, Collection<T> values) {
        try {
            return listOperations.leftPushAll(key.getValue(), values);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> Long rightPush(RedisKeyObject key, T obj) {
        try {
            return listOperations.rightPush(key.getValue(), obj);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> Long rightPushAll(RedisKeyObject key, Collection<T> values) {
        try {
            return listOperations.rightPushAll(key.getValue(), values);
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> T get(RedisKeyObject key, Class<T> classType) {
        try {
            Object obj = valueOperations.get(key.getValue());
            if(null != obj) {
                return JsonUtil.toObject(obj.toString(), classType);
            }
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    public String getString(RedisKeyObject key) {
        try {
            return (String) valueOperations.get(key.getValue());
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> T hashGet(RedisKeyObject key, String hashKey, Class<T> classType) {
        try {
            Object obj = hashOperations.get(key.getValue(), hashKey);
            if(null != obj) {
                return JsonUtil.toObject(obj.toString(), classType);
            }
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected Map<String, Object> hashGetEntry(RedisKeyObject key) {
        try {
            return hashOperations.entries(key.getValue());
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> T leftPop(RedisKeyObject key, Class<T> classType) {
        try {
            Object obj = listOperations.leftPop(key.getValue());
            if(null != obj) {
                return JsonUtil.toObject(obj.toString(), classType);
            }
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected <T> T rightPop(RedisKeyObject key, Class<T> classType) {
        try {
            Object obj = listOperations.rightPop(key.getValue());
            if(null != obj) {
                return JsonUtil.toObject(obj.toString(), classType);
            }
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    protected Boolean rightPopAndLeftPush(RedisKeyObject sourceKey, RedisKeyObject destinationKey) {
        try {
            Object obj = listOperations.rightPopAndLeftPush(sourceKey.getValue(), destinationKey.getValue());
            if(null != obj) {
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }

    /**
     * 获取key的过期时间
     */
    protected Long getExpireTime(RedisKeyObject key) {
        try {
            return redisTemplate.opsForValue().getOperations().getExpire(key.getValue());
        } catch (Exception e) {
            log.error("Redis Error", e);
        }
        return null;
    }


    /**
     * setnx exptime
     */
    protected Boolean setIfAbsent(RedisKeyObject key, String value, long expireTime) {
        return redisTemplate.opsForValue().setIfAbsent(key.getValue(),value,expireTime, TimeUnit.SECONDS);

    }

    public boolean publish(String topic,Object obj) {
        if (obj instanceof String) {
            redisTemplate.convertAndSend(topic,obj.toString());
        } else {
            String text = JsonUtil.toJSONString(obj);
            redisTemplate.convertAndSend(topic,text);
        }
        return true;
    }
    
}
